using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.Json;
using MultiAgentsClient.Service.WorkflowEngine;
using MultiAgentsClient.Service.WorkflowEngine.BaseNodes;
using MultiAgentsClient.Service.WorkflowEngine.BuiltInNodes;
using MultiAgentsShared;

public class WorkflowNodeConfigToJson
{
    private static BindingFlags JsonBindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;



    public static string ConvertAllNodeConfigsToJson()
    {
        var baseType = typeof(BaseNode);
        var assembly = Assembly.GetAssembly(baseType); // Or Assembly.GetExecutingAssembly() if they're in the same assembly

        var nodeClasses = assembly.GetTypes().Where(type => type.IsSubclassOf(baseType) && !type.IsAbstract);


        using (var stream = new MemoryStream())
        {
            var writerOption = new JsonWriterOptions()
            {
                Indented = true,
            };

            using (var writer = new Utf8JsonWriter(stream, writerOption))
            {
                writer.WriteStartArray();
                foreach (Type nodeType in nodeClasses)
                {
                    WriteNode(writer, nodeType);
                }
                writer.WriteEndArray();
            }

            // Convert the MemoryStream contents to a string
            return Encoding.UTF8.GetString(stream.ToArray());
        }
    }

    private static void WriteNode(Utf8JsonWriter writer, Type nodeType)
    {
        writer.WriteStartObject();
        writer.WriteString("NodeTypeName", nodeType.Name);
        writer.WriteString("Type", nodeType.Name);
        writer.WriteString("TypeDetail", nodeType.AssemblyQualifiedName);
        WriteNodeOutput(writer, nodeType);

        WriteFieldsPropertiesToArray("dynamicElements", writer, nodeType, true);

        var portDefAttribute = (NodeGraphPortDefAttribute)nodeType.GetCustomAttribute(typeof(NodeGraphPortDefAttribute), false);
        writer.WriteNumber("InputPortsCount", portDefAttribute.InputPorts);
        writer.WriteNumber("OutputPortsCount", portDefAttribute.OutputPorts);

        writer.WriteEndObject();
    }

    private static void WriteNodeOutput(Utf8JsonWriter writer, Type nodeType)
    {
        writer.WriteStartArray("OutputVariable");

        foreach (PropertyInfo property in nodeType.GetProperties(JsonBindingFlags))
        {
            if (Attribute.IsDefined(property, typeof(NodeGraphOutputAttribute), true))
            {
                string memberName = property.Name;
                //Type memberType = property.PropertyType; //TODO: maybe we need type for display purpose
                writer.WriteStringValue(memberName);
            }
        }

        foreach (FieldInfo field in nodeType.GetFields(JsonBindingFlags))
        {
            if (Attribute.IsDefined(field, typeof(NodeGraphOutputAttribute), true))
            {
                string memberName = field.Name;
                //Type memberType = field.FieldType;
                writer.WriteStringValue(memberName);
            }
        }

        writer.WriteEndArray();

    }

    /// <param name="arrayName"></param>
    /// <param name="writer"></param>
    /// <param name="classType"></param>
    /// <param name="topLevel">If its topLevel, then check for NodeGraphParamAttribute otherwise not</param>
    private static void WriteFieldsPropertiesToArray(string arrayName, Utf8JsonWriter writer, Type classType, bool topLevel)
    {
        // Inspect and serialize properties
        writer.WriteStartArray(arrayName);

        foreach (PropertyInfo property in classType.GetProperties(JsonBindingFlags))
        {
            if (Attribute.IsDefined(property, typeof(NodeGraphParamAttribute)) || topLevel == false)
            {
                string memberName = property.Name;
                Type memberType = property.PropertyType;
                SerializeMember(writer, memberName, memberType);
            }
        }

        // Inspect and serialize fields
        foreach (FieldInfo field in classType.GetFields(JsonBindingFlags))
        {
            if (field.IsDefined(typeof(CompilerGeneratedAttribute), false))
            {
                // This is a compiler-generated backing field from getter/setter, skip serialization.
                continue;
            }

            if (Attribute.IsDefined(field, typeof(NodeGraphParamAttribute)) || topLevel == false)
            {
                string memberName = field.Name;
                Type memberType = field.FieldType;
                SerializeMember(writer, memberName, memberType);
            }
        }


        writer.WriteEndArray();
    }

    private static void SerializeMember(Utf8JsonWriter writer, string name, Type memberType)
    {
        if (memberType.IsEnum)
        {
            SerializeEnum(writer, name, memberType);
        }
        else if (memberType == typeof(string))
        {
            SerializeString(writer, name);
        }
        else if (memberType == typeof(bool))
        {
            SerializeBool(writer, name);
        }
        else if (memberType.IsGenericType && memberType.GetGenericTypeDefinition() == typeof(ListVariable<>))
        {
            SerializeListVariable(writer, name);
        }
        else if (memberType
                .GetInterfaces()
                .Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>)))
        {
            Type? genericTypeArgument = GetEnumerableType(memberType);

            // Proceed only if you successfully retrieved the generic type argument
            if (genericTypeArgument != null)
            {
                SerializeList(writer, name, genericTypeArgument);  // Adjust 'SerializeList' to accept 'genericTypeArgument'
            }
            else
            {
                Debug.LogError("Failed to find element type");
            }
        }
        // Extend this method for additional types and custom logic.
    }

    private static void SerializeEnum(Utf8JsonWriter writer, string name, Type memberType)
    {
        writer.WriteStartObject();
        writer.WriteString("type", "enum");
        writer.WriteString("name", name);

        //Its used for showing the selected nestedClass within a list or something
        //TODO: maybe we should not included selection in this json data, as its pure config
        writer.WriteNumber("value", 0);

        writer.WriteStartArray("elementList");
        foreach (var val in Enum.GetNames(memberType))
        {
            writer.WriteStringValue(val);
        }
        writer.WriteEndArray();
        writer.WriteEndObject();
    }

    private static void SerializeBool(Utf8JsonWriter writer, string name)
    {
        writer.WriteStartObject();
        writer.WriteString("type", "bool");
        writer.WriteString("name", name);

        //Its used for showing the selected nestedClass within a list or something
        //TODO: maybe we should not included selection in this json data, as its pure config
        writer.WriteBoolean("value", false);

        writer.WriteEndObject();
    }

    private static void SerializeListVariable(Utf8JsonWriter writer, string name)
    {
        writer.WriteStartObject();
        writer.WriteString("type", "listVariable");
        writer.WriteString("name", name);
        writer.WriteString("value", "");
        writer.WriteEndObject();
    }

    private static void SerializeString(Utf8JsonWriter writer, string name)
    {
        writer.WriteStartObject();
        writer.WriteString("type", "text");
        writer.WriteString("name", name);

        //Its used for showing the selected nestedClass within a list or something
        //TODO: maybe we should not included selection in this json data, as its pure config
        writer.WriteString("value", "");

        writer.WriteEndObject();
    }

    private static void SerializeList(Utf8JsonWriter writer, string name, Type elementType)
    {
        writer.WriteStartObject();
        writer.WriteString("type", "editableList");
        writer.WriteString("name", name);

        //Its used for showing the selected nestedClass within a list or something
        //TODO: maybe we should not included selection in this json data, as its pure config
        writer.WriteStartArray("value");
        writer.WriteEndArray();

        //elementList
        WriteFieldsPropertiesToArray("elementList", writer, elementType, false);

        writer.WriteEndObject();
    }

    private static Type? GetEnumerableType(Type type)
    {
        foreach (var iface in type.GetInterfaces())
        {
            if (iface.IsGenericType && iface.GetGenericTypeDefinition() == typeof(IEnumerable<>))
            {
                return iface.GetGenericArguments()[0]; // Returns the type T used in IEnumerable<T>
            }
        }
        return null; // Indicates that no IEnumerable<T> interface was found
    }
}
